package com.jzo2o.health.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.api.trade.enums.RefundStatusEnum;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.expcetions.ServerErrorException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.model.OrderUpdateStatusDTO;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.Orders;
import com.jzo2o.health.model.domain.OrdersCancelled;
import com.jzo2o.health.model.domain.OrdersRefund;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.service.IOrderCancelService;
import com.jzo2o.health.service.IOrderCommonService;
import com.jzo2o.health.service.IOrderRefundService;
import com.jzo2o.health.service.IReservationSettingService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 订单退款相关业务层实现
 * @author JIAN
 */
@Service
public class OrderRefundServiceImpl extends ServiceImpl<OrdersRefundMapper, OrdersRefund> implements IOrderRefundService {
    @Resource
    private IOrderCommonService orderCommonService;
    @Resource
    private IOrderCancelService orderCancelService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private RefundRecordApi refundRecordApi;
    @Resource
    private IReservationSettingService reservationSettingService;

    @Override
    @Transactional
    public void refundOrder(OrdersCancelReqDTO ordersCancelReqDTO) {
        CurrentUserInfo currentUser = UserThreadLocal.currentUser();
        if (ObjectUtils.isEmpty(currentUser) || ObjectUtils.isEmpty(currentUser.getId())) {
            throw new ForbiddenOperationException("无法获取用户信息无法取消");
        }

        Long orderId = ordersCancelReqDTO.getId();
        Orders orders = orderCommonService.getById(orderId);
        if (ObjectUtils.isEmpty(orders)) {
            throw new ForbiddenOperationException("订单不存在无法取消");
        }

        if (orders.getOrderStatus() != OrderStatusEnum.WAITING_CHECKUP || orders.getPayStatus() != OrderPayStatusEnum.PAY_SUCCESS) {
            throw new ForbiddenOperationException("订单状态错误无法取消");
        }

        if (!orderCommonService.updateStatus(OrderUpdateStatusDTO.builder()
                .id(orderId)
                .originStatus(OrderStatusEnum.WAITING_CHECKUP)
                .targetStatus(OrderStatusEnum.CLOSED)
                .payStatus(OrderPayStatusEnum.REFUNDING)
                .build())) {
            throw new DBException("更新订单表失败");
        }

        if (!orderCancelService.save(OrdersCancelled.builder()
                .id(orderId)
                .cancelTime(LocalDateTime.now())
                .cancelReason(ordersCancelReqDTO.getCancelReason())
                .cancellerName(currentUser.getName())
                .cancellerId(currentUser.getId())
                .cancellerType(currentUser.getUserType())
                .build())) {
            throw new DBException("更新取消表失败");
        }

        OrdersRefund ordersRefund = OrdersRefund.builder()
                .id(orderId)
                .tradingOrderNo(orders.getTradingOrderNo())
                .realPayAmount(/*orders.getSetmealPrice()*/ new BigDecimal("0.01"))
                .build();
        if (!SqlHelper.retBool(baseMapper.insert(ordersRefund))) {
            throw new DBException("更新退款表失败");
        }

        // 已预约人数 - 1
        reservationSettingService.plusReservationCount(orders.getReservationDate(), -1);

        new Thread(() -> this.refundOrder(ordersRefund)).start();
    }

    @Override
    public void refundOrder(OrdersRefund ordersRefund) {
        ExecutionResultResDTO executionResultResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
        if (ObjectUtils.isEmpty(executionResultResDTO)) {
            throw new ServerErrorException("退款接口调用失败请重试");
        }

        Integer refundStatus = executionResultResDTO.getRefundStatus();
        OrderPayStatusEnum payStatus;
        if (RefundStatusEnum.SUCCESS.getCode().equals(refundStatus)) {
            payStatus = OrderPayStatusEnum.REFUND_SUCCESS;
        } else if (RefundStatusEnum.FAIL.getCode().equals(refundStatus)) {
            payStatus = OrderPayStatusEnum.REFUND_FAIL;
        } else {
            return;
        }

        transactionTemplate.executeWithoutResult(status -> {
            if (!orderCommonService.updateStatus(OrderUpdateStatusDTO.builder()
                    .id(ordersRefund.getId())
                    .targetStatus(OrderStatusEnum.CLOSED)
                    .refundNo(executionResultResDTO.getRefundNo())
                    .refundId(executionResultResDTO.getRefundId())
                    .payStatus(payStatus)
                    .build())) {
                throw new DBException("更新订单表失败");
            }

            if (!SqlHelper.retBool(baseMapper.deleteById(ordersRefund.getId()))) {
                throw new DBException("更新退款表失败");
            }
        });
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<OrdersRefund> getLatestRefundInfo(Integer count) {
        return lambdaQuery()
                .orderByDesc(OrdersRefund::getCreateTime)
                .last("LIMIT " + count)
                .list();
    }
}